# S模式下的中断或异常入口程序
.global kerneltrap
.global kernelvec
.align 4
kernelvec:
    # 分配256字节的栈空间用于保存重要寄存器
    addi sp, sp, -256

    # 保存重要的调用者的寄存器
    # Store Doubleword，将寄存器值存储到内存(sp + offset)中
    sd ra, 0(sp)      # 返回地址
    sd sp, 8(sp)      # 栈指针
    sd gp, 16(sp)     # 全局指针
    sd tp, 24(sp)     # 线程指针
    sd t0, 32(sp)     # 临时寄存器t0
    sd t1, 40(sp)     # 临时寄存器t1
    sd t2, 48(sp)     # 临时寄存器t2
    sd a0, 72(sp)     # 参数/返回值寄存器a0
    sd a1, 80(sp)     # 参数寄存器a1
    sd a2, 88(sp)     # 参数寄存器a2
    sd a3, 96(sp)     # 参数寄存器a3
    sd a4, 104(sp)    # 参数寄存器a4
    sd a5, 112(sp)    # 参数寄存器a5
    sd a6, 120(sp)    # 参数寄存器a6
    sd a7, 128(sp)    # 参数寄存器a7
    sd t3, 216(sp)    # 临时寄存器t3
    sd t4, 224(sp)    # 临时寄存器t4
    sd t5, 232(sp)    # 临时寄存器t5
    sd t6, 240(sp)    # 临时寄存器t6

    # 调用trap.c中的中断处理函数
    # 实际是用硬件向量表保存每个中断的入口地址
    # 这里采用xv6的模式，统一用kerneltrap处理中断和异常
    call kerneltrap

    # 恢复之前保存的寄存器
    # Load Doubleword，将内存(sp + offset)中的值加载到寄存器
    ld ra, 0(sp)      # 恢复返回地址
    ld sp, 8(sp)      # 恢复栈指针
    ld gp, 16(sp)     # 恢复全局指针
    # 注意：不恢复tp（包含hartid），以防CPU迁移
    ld t0, 32(sp)     # 恢复临时寄存器
    ld t1, 40(sp)
    ld t2, 48(sp)
    ld a0, 72(sp)     # 恢复参数/返回值寄存器
    ld a1, 80(sp)
    ld a2, 88(sp)
    ld a3, 96(sp)
    ld a4, 104(sp)
    ld a5, 112(sp)
    ld a6, 120(sp)
    ld a7, 128(sp)
    ld t3, 216(sp)    # 恢复临时寄存器
    ld t4, 224(sp)
    ld t5, 232(sp)
    ld t6, 240(sp)

    # 释放栈空间
    addi sp, sp, 256
    
    # 从中断或异常返回
    # 注意：是从S模式返回，因为在start中，M模式将中断和异常都委托给了S模式
    sret